<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.26">
<title>Simple ToDo-List</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
/* Uncomment the following line when using as a custom stylesheet */
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
a{background:none}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
b,strong{font-weight:bold}
abbr{font-size:.9em}
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
dfn{font-style:italic}
hr{height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
audio,video{display:inline-block}
audio:not([controls]){display:none;height:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type=checkbox],input[type=radio]{padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,::before,::after{box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:0}
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ul.square{list-style-type:square}
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt{background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
:not(pre).nobreak{word-wrap:normal}
:not(pre).nowrap{white-space:nowrap}
:not(pre).pre-wrap{white-space:pre-wrap}
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
#header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px}
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #e7e7e9}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
details{margin-left:1.25rem}
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
details>summary::-webkit-details-marker{display:none}
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child,.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
.listingblock>.content{position:relative}
.listingblock pre>code{display:block}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.prettyprint{background:#f7f7f8}
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
pre.prettyprint li code[data-lang]::before{opacity:1}
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
table.linenotable td.code{padding-left:.75em}
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>*>tr>*{border-width:1px}
table.grid-cols>*>tr>*{border-width:0 1px}
table.grid-rows>*>tr>*{border-width:1px 0}
table.frame-all{border-width:1px}
table.frame-ends{border-width:1px 0}
table.frame-sides{border-width:0 1px}
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
li>p:empty:only-child::before{content:"";display:inline-block}
ul.checklist>li>p:first-child{margin-left:-1em}
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
td.hdlist2{word-wrap:anywhere}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
.imageblock.left{margin:.25em .625em 1.25em 0}
.imageblock.right{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background:#00fafa}
.black{color:#000}
.black-background{background:#000}
.blue{color:#0000bf}
.blue-background{background:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background:#fa00fa}
.gray{color:#606060}
.gray-background{background:#7d7d7d}
.green{color:#006000}
.green-background{background:#007d00}
.lime{color:#00bf00}
.lime-background{background:#00fa00}
.maroon{color:#600000}
.maroon-background{background:#7d0000}
.navy{color:#000060}
.navy-background{background:#00007d}
.olive{color:#606000}
.olive-background{background:#7d7d00}
.purple{color:#600060}
.purple-background{background:#7d007d}
.red{color:#bf0000}
.red-background{background:#fa0000}
.silver{color:#909090}
.silver-background{background:#bcbcbc}
.teal{color:#006060}
.teal-background{background:#007d7d}
.white{color:#bfbfbf}
.white-background{background:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]{border-bottom:1px dotted}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#header,#content,#footnotes,#footer{max-width:none}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
</head>
<body class="article toc2 toc-left">
<div id="header">
<h1>Simple ToDo-List</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_before_we_start">Before we start</a>
<ul class="sectlevel2">
<li><a href="#_communitytoolkit_mvvm">CommunityToolkit.MVVM</a></li>
<li><a href="#_preparation_work">Preparation work</a></li>
</ul>
</li>
<li><a href="#_the_solution">The Solution</a>
<ul class="sectlevel2">
<li><a href="#_step_1_create_and_set_up_a_new_project">Step 1: Create and set up a new project</a></li>
<li><a href="#_step_2_set_up_the_model">Step 2: Set up the Model</a></li>
<li><a href="#_step_3_setup_the_viewmodel">Step 3: Setup the ViewModel</a></li>
<li><a href="#_step_4_set_up_the_view">Step 4: Set up the View</a></li>
<li><a href="#_step_5_optional_add_some_styles_to_improve_the_ui">Step 5 (Optional): Add some Styles to improve the UI</a></li>
<li><a href="#_step_7_optional_data_persistence">Step 7 (Optional): Data Persistence</a></li>
</ul>
</li>
<li><a href="#_related">Related</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This example will show you how a simple ToDo-List App can be build using Avalonia in combination with the MVVM-Community-Toolkit.</p>
</div>
<!-- toc disabled -->
<h3 id="_difficulty" class="discrete">Difficulty</h3>
<div class="paragraph">
<p>🐥 Easy 🐥</p>
</div>
<h3 id="_buzz_words" class="discrete">Buzz-Words</h3>
<div class="paragraph">
<p>ToDo-List, complete app, MVVM, CommunityToolkit.MVVM, source generator, styles, commands</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_before_we_start">Before we start</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This sample assumes that you have a basic knowledge about the following topics:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>How to create a <a href="https://docs.avaloniaui.net/docs/get-started/test-drive/create-a-project">[new Avalonia project]</a></p>
</li>
<li>
<p>Some basics about C# and <a href="https://docs.avaloniaui.net/docs/get-started/test-drive/">[XAML]</a></p>
</li>
<li>
<p>What the <a href="../../MVVM/BasicMvvmSample">[MVVM -pattern]</a> (Model-View-ViewModel) is and how it works</p>
</li>
<li>
<p>What a <a href="../../MVVM/CommandSample">[Command]</a> is and how it works</p>
</li>
<li>
<p>What a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=net-8.0">[ObservableCollection]</a> is and how it works</p>
</li>
</ul>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Some sections are optional. You can skip these if you want to.
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_communitytoolkit_mvvm">CommunityToolkit.MVVM</h3>
<div class="paragraph">
<p>The <a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/">[CommunityToolkit.MVVM]</a>-package is one of many third-party packages for MVVM-Apps. We will use it in this sample as it is very lightweight. In addition, it comes with built-in source generators which allows us to write less boilerplate code.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>For example, we can annotate private fields with the <code>[ObservableProperty]</code>-attribute which will let the source generator create a property for us:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">// Using the source generator:
[ObservableProperty]
private bool _isChecked;

// Without using the source generator we would need to write:
private bool _isChecked;

public bool IsChecked
{
    get { return _isChecked; }
    set { SetProperty(ref _isChecked, value); }
}</code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you want to learn more about how these source generators work, please see <a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/overview">[here]</a> and <a href="https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview">[here]</a>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_preparation_work">Preparation work</h3>
<div class="paragraph">
<p>Before we start with the actual implementation of the App, we need to define a list of requirements and a sketch of the UI.</p>
</div>
<div class="paragraph">
<p><strong>Must-Haves</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>The user must be able to view a list of to-do items</p>
</li>
<li>
<p>All items must be easily discoverable</p>
</li>
<li>
<p>The user must be able to check and uncheck the to-do items</p>
</li>
<li>
<p>The user must be able to add new items</p>
</li>
<li>
<p>The user must be able to delete an existing item</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Nice-To-Haves</strong></p>
</div>
<div class="ulist">
<ul>
<li>
<p>The user should have a visual feedback when hovering a to-do item</p>
</li>
<li>
<p>The user should have a visual feedback if a new item can be added or not</p>
</li>
<li>
<p>The user should be able to save and load a to-do-list (not a scope of this tutorial)</p>
</li>
<li>
<p>The user should be able to edit an existing item (not a scope of this tutorial)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p><strong>Draft UI Layout</strong></p>
</div>
<div class="paragraph">
<p>It would be nice to have a single page user interface (UI) where the user can interact with the to-do list. We will use the operating system&#8217;s window frame to present our App. In the bottom of the page, the user will have a <code>TextBox</code> where they can type any new item content to add. A <code>Watermark</code> or <code>ToolTip</code> shall indicate that visually. As soon as we receive valid input for a new item, a <code>Button</code> next to the input field should be enabled. If the user invokes this <code>Button</code>, the new item will be added into our list. The list itself will fill the rest of our available space. In case the space is not enough to render all items, a <code>ScrollBar</code> will enable vertical scrolling.
Each item in the list is going to be represented by a <code>CheckBox</code> followed by the content and a <code>Button</code> to delete the item.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/Sketch.png" alt="Sketch of the UI">
</div>
<div class="title">Figure 1. Sketch of the UI</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_solution">The Solution</h2>
<div class="sectionbody">
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
In this sample we will use the MVVM (Model-View-ViewModel) approach, where we will start with the Model and ViewModel first.
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="_step_1_create_and_set_up_a_new_project">Step 1: Create and set up a new project</h3>
<div class="paragraph">
<p>Choose your favorite IDE to create a new Avalonia-MVVM project.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Depending on the IDE used to create your project, you may see a file structure that differs from the one seen in this tutorial.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>By default, the Avalonia-MVVM template installs <code>ReactiveUI.Avalonia</code> as the MVVM framework. However, we want to use another MVVM framework. Therefor we will uninstall that package and instead install the <code>CommunityToolkit.Mvvm</code> package. We need to edit two files to get the changes applied:</p>
</div>
<div class="paragraph">
<p><strong>Program.cs</strong>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-diff" data-lang="diff">    using Avalonia;
-   using ReactiveUI.Avalonia; // remove this using
    using System;

[...]

    public static AppBuilder BuildAvaloniaApp()
        =&gt; AppBuilder.Configure&lt;App&gt;()
            .UsePlatformDetect()
            .WithInterFont()
            .LogToTrace();
-           .UseReactiveUI(); // remove this line</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>ViewModels/ViewModelBase</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-diff" data-lang="diff">-   using ReactiveUI;
+   using CommunityToolkit.Mvvm.ComponentModel;

    namespace GetStartedApp.ViewModels;

-   public class ViewModelBase : ReactiveObject
+   public class ViewModelBase : ObservableObject
    {
    }</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_2_set_up_the_model">Step 2: Set up the Model</h3>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
In our case we need the model for I/O operations. If you have no use for the model in your own App, feel free to skip that part.
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
In case the folder <code>Models</code> is missing on your side, just add it to your project.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The Model will be quite simple in our case. We want to have one class called <code>ToDoItem</code>, which has two <code>Properties</code>. This model will also be used to save and restore the users ToDo-List later on. Inside the folder <code>Models</code>, add a new class called <code>ToDoItem</code>:</p>
</div>
<div class="listingblock">
<div class="title">Models/ToDoItem.cs</div>
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// This is our Model for a simple ToDoItem.
/// &lt;/summary&gt;
public class ToDoItem
{
    /// &lt;summary&gt;
    /// Gets or sets the checked status of each item
    /// &lt;/summary&gt;
    public bool IsChecked { get; set; }

    /// &lt;summary&gt;
    /// Gets or sets the content of the to-do item
    /// &lt;/summary&gt;
    public string? Content { get; set; }
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_3_setup_the_viewmodel">Step 3: Setup the ViewModel</h3>
<div class="sect3">
<h4 id="_the_todoitem_viewmodel">The ToDoItem-ViewModel</h4>
<div class="paragraph">
<p>Our next task is to create a <code>ViewModel</code> for our to-do-items, which we will use as an intermediate layer between the <code>View</code> and the <code>Model</code>. Inside the folder <code>ViewModels</code> add a new class <code>ToDoItemViewModel</code> which inherits <code>ViewModelBase</code>.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
If you want to use the source generators, remember to mark the class as <code>partial</code>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">ViewModels/ToDoItemViewModel.cs</div>
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// This is a ViewModel which represents a &lt;see cref="Models.ToDoItem"/&gt;
/// &lt;/summary&gt;
public partial class ToDoItemViewModel : ViewModelBase
{
    /// &lt;summary&gt;
    /// Gets or sets the checked status of each item
    /// &lt;/summary&gt;
    [ObservableProperty]
    private bool _isChecked;

    /// &lt;summary&gt;
    /// Gets or sets the content of the to-do item
    /// &lt;/summary&gt;
    [ObservableProperty]
    private string? _content;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Our <code>ViewModel</code> is not yet connected to our <code>Model</code>. In order to create a new <code>ToDoItemViewModel</code> from an existing <code>ToDoItem</code>, we can add a constructor that takes the <code>ToDoItem</code> as an argument.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
We also want to be able to create a new, empty <code>ToDoItemViewModel</code>. Therefore, we also add a parameterless constructor.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// Creates a new blank ToDoItemViewModel
/// &lt;/summary&gt;
public ToDoItemViewModel()
{
    // empty
}

/// &lt;summary&gt;
/// Creates a new ToDoItemViewModel for the given &lt;see cref="Models.ToDoItem"/&gt;
/// &lt;/summary&gt;
/// &lt;param name="item"&gt;The item to load&lt;/param&gt;
public ToDoItemViewModel(ToDoItem item)
{
    // Init the properties with the given values
    IsChecked = item.IsChecked;
    Content = item.Content;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Okay, now we also need a way to get the updated <code>Model</code> back, if the user made some changes. We can do this for example using a read-only property or a method like shown below:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// Gets a ToDoItem of this ViewModel
/// &lt;/summary&gt;
/// &lt;returns&gt;The ToDoItem&lt;/returns&gt;
public ToDoItem GetToDoItem()
{
    return new ToDoItem()
    {
        IsChecked = this.IsChecked,
        Content = this.Content
    };
}</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="_the_mainviewmodel">The MainViewModel</h4>
<div class="paragraph">
<p>Depending on the template used to create the project you should see a file called <code>MainViewModel</code> or <code>MainWindowViewModel</code>. Open this file in order to edit it.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
If you see a property called <code>Greetings</code>, feel free to delete it as we don&#8217;t need that in our App.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Let&#8217;s add an <code>ObservableCollection</code> called <code>ToDoItems</code>. As the collection will notify the UI whenever an item was added or removed, we can make this property readonly. Thus, a getter is enough here.</p>
</div>
<div class="listingblock">
<div class="title">ViewModels/MainViewModel.cs</div>
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// Gets a collection of &lt;see cref="ToDoItem"/&gt; which allows adding and removing items
/// &lt;/summary&gt;
public ObservableCollection&lt;ToDoItemViewModel&gt; ToDoItems { get; } = new ObservableCollection&lt;ToDoItemViewModel&gt;();</code></pre>
</div>
</div>
<div class="paragraph">
<p>Well, now we have a collection of <code>ToDoItems</code> but how can we add new items to it? In our case this is quite simple as we only expect a non-empty <code>string</code> as content to construct a new item. We will add a helper property called <code>NewItemContent</code>. If that <code>string</code> is not empty, a command called <code>AddItemCommand</code> will be enabled.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The command and the properties will be created using the source generator provided by the MVVM-toolkit we use. Remember to make the <code>MainViewModel</code> <code>partial</code>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// Gets or set the content for new Items to add. If this string is not empty, the AddItemCommand will be enabled automatically
/// &lt;/summary&gt;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(AddItemCommand))] // This attribute will invalidate the command each time this property changes
private string? _newItemContent;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Next step is to create a method or a property that returns a <code>bool</code> indicating whether the <code>AddItemCommand</code> can execute:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// Returns if a new Item can be added. We require to have the NewItem some Text
/// &lt;/summary&gt;
private bool CanAddItem() =&gt; !string.IsNullOrWhiteSpace(NewItemContent);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Last but not least we can add the <code>Command</code>. If we annotate a <code>void</code> or a <code>Task</code> with the <a href="https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand">[<code>RelayCommand</code>-attribute]</a>, a new <code>RelayCommand</code> will be generated for us. In our case we use a <code>void</code> called <code>AddItem</code> which will add a new item into <code>ToDoItems</code>-collection. After that we want to reset the <code>NewItemContent</code>, so that the input field is cleared for the next item to be added.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// This command is used to add a new Item to the List
/// &lt;/summary&gt;
[RelayCommand (CanExecute = nameof(CanAddItem))]
private void AddItem()
{
    // Add a new item to the list
    ToDoItems.Add(new ToDoItemViewModel() {Content = NewItemContent});

    // reset the NewItemContent
    NewItemContent = null;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Adding items is possible now, but we also want to be able to remove items. So we will add another <code>Command</code> for that. However, we need to know which item to remove. So we will pass the item to remove as a <code>CommandParameter</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
According to our App draft, we want to add the <code>Delete-Button</code> next to each item. Therefore, we can be sure that always a valid <code>CommandParameter</code> is sent to the <code>Command</code>. Therefore, we don&#8217;t need to set <code>CanExecute</code> in this case.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-cs" data-lang="cs">/// &lt;summary&gt;
/// Removes the given Item from the list
/// &lt;/summary&gt;
/// &lt;param name="item"&gt;the item to remove&lt;/param&gt;
[RelayCommand]
private void RemoveItem(ToDoItemViewModel item)
{
    // Remove the given item from the list
    ToDoItems.Remove(item);
}</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_4_set_up_the_view">Step 4: Set up the View</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Depending on the template you used to create your project, you may see a file called <code>MainView</code> alongside <code>MainWindow</code>. In this case, please use <code>MainView</code> to add the content shown below. <code>MainWindow</code> will present this view for you.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Let&#8217;s recall the App-design we planned:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/Sketch.png" alt="Our sketch of the App-design">
</div>
<div class="title">Figure 2. Our sketch of the App-design</div>
</div>
<div class="paragraph">
<p>As shown above we need a header at the top, a presentation of the items in the middle (which takes as much space as possible) and a footer with an input-field for adding new items. In Avalonia we use <a href="https://docs.avaloniaui.net/docs/basics/user-interface/building-layouts/#panels">[<code>Panels</code>]</a> to achieve different layouts. In our case we can use a <code>DockPanel</code> or a <code>Grid</code>. We will use a <code>Grid</code> as this offers the most flexible layout.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>A <code>Grid-Row</code> or <code>Grid-Column</code> can have different <code>GridSizes</code>:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.avaloniaui.net/docs/reference/controls/grid/#absolute-size-definitions">Absolute Size Definition</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/docs/reference/controls/grid/#proportional-size-definitions">Proportional Size Definitions</a></p>
</li>
<li>
<p><a href="https://docs.avaloniaui.net/docs/reference/controls/grid/#automatic-size-definitions">Automatic Size Definitions</a></p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
You can set <code>Grid.Row</code> and <code>Grid.Column</code>-attached properties on every child control to define the exact cell where the control should be shown.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Here is our basic layout:</p>
</div>
<div class="listingblock">
<div class="title">Views/MainWindow.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;!-- Leave the root-Node untouched beside setting Width, Height and Padding --&gt;
&lt;Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:SimpleToDoList.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Width="300" Height="500" Padding="4"
        x:Class="SimpleToDoList.Views.MainWindow"
        x:DataType="vm:MainViewModel"
        Icon="/Assets/avalonia-logo.ico"
        Title="SimpleToDoList"&gt;
    &lt;!-- We give a name to the root grid in order to access it later --&gt;
    &lt;Grid RowDefinitions="Auto, *, Auto"
          x:Name="Root"&gt;

        &lt;!-- This is our title text block.  --&gt;
        &lt;TextBlock Text="My ToDo-List" /&gt;

        &lt;ScrollViewer Grid.Row="1"&gt;
            &lt;!-- This ItemsControl show all added ToDoItems. --&gt;
            &lt;!-- It needs to be placed inside a ScrollViewer because other than a ListBox it has not its own --&gt;
            &lt;ItemsControl ItemsSource="{Binding ToDoItems}"&gt;
            &lt;/ItemsControl&gt;
        &lt;/ScrollViewer&gt;

        &lt;!-- This TextBox can be used to add new ToDoItems --&gt;
        &lt;TextBox Grid.Row="2"
                 Text="{Binding NewItemContent}"
                 Watermark="Add a new Item"&gt;
        &lt;/TextBox&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We are now good to go and run our App for the first time. You should see something similar to this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/First_Run.png" alt="First App run">
</div>
<div class="title">Figure 3: First App run</div>
</div>
<div class="paragraph">
<p>We can see the title <code>TextBlock</code> is there and also the entry field for new items to add. We can add some text into the <code>TextBox</code>, however we have no button to add it into our list yet. Let&#8217;s fix that.</p>
</div>
<div class="paragraph">
<p>In Avalonia a TextBox has the option to add <code>InnerLeftContent</code> and <code>InnerRightContent</code>, which can be used to add any content you like. For example, we can add a button to it. The <code>Button</code> will execute the <code>AddItemCommand</code>. For convenience, we also want to allow adding items using the keyboard. That is what <a href="https://docs.avaloniaui.net/docs/concepts/input/hotkeys">[<code>KeyBindings</code> and <code>HotKeys</code>]</a> can be used for.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
We use <code>KeyBindings</code> here as a <code>HotKey</code> would be available for the whole view, where we want it only to work when the <code>TextBox</code> is focused.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Here is how our modified input box looks like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;TextBox Grid.Row="2"
         Text="{Binding NewItemContent}"
         Watermark="Add a new Item"&gt;
    &lt;TextBox.InnerRightContent&gt;
        &lt;Button Command="{Binding AddItemCommand}" &gt;
            Add
            &lt;!-- &lt;PathIcon Data="{DynamicResource AcceptIconData}" Foreground="Green" /&gt; --&gt;
        &lt;/Button&gt;
    &lt;/TextBox.InnerRightContent&gt;
    &lt;!--  KeyBindings allow us to define keyboard gestures  --&gt;
    &lt;TextBox.KeyBindings&gt;
        &lt;KeyBinding Command="{Binding AddItemCommand}" Gesture="Enter" /&gt;
    &lt;/TextBox.KeyBindings&gt;
&lt;/TextBox&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now, if you run your App again, you will notice that we have a Button which is grayed out if the input field is empty and gets enabled as soon as you start typing. Invoking the <code>Button</code> will add a new item to our list above.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/Second_Run.png" alt="Second App run">
</div>
<div class="title">Figure 4: Second App run</div>
</div>
<div class="paragraph">
<p>Great, we can add new items to our list. However, they don&#8217;t display themselves as we wanted them to. Instead of showing the content, we see the name of the <code>ItemViewModel</code>. We can fix that by defining the <code>ItemTemplate</code> for the <code>ItemsControl</code>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you want to learn more about <code>DataTemplates</code>, see these samples: <a href="https://github.com/AvaloniaUI/Avalonia.Samples?tab=readme-ov-file#%EF%B8%8F-datatemplate-samples">[DataTemplate-Samples]</a>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Our <code>DataTemplate</code> uses a <code>CheckBox</code> where the <code>Content</code> is bound to <code>ToDoItemViewModel.Content</code> and <code>IsChecked</code> is bound to <code>ToDoItemViewModel.IsChecked</code>. Next to it, we will add a <code>Button</code> which is there to delete the given item. The <code>Command</code> is bound to the <code>MainViewModel.DeleteCommand</code> and the <code>CommandParameter</code> is the <code>ToDoItemViewModel</code> itself.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Inside the <code>ItemTemplate</code> we can only use members of our <code>ItemViewModel</code>. However, the <code>DeleteCommand</code> is part of our <code>MainViewModel</code>. We can access this by accessing the parents or named controls <code>DataContext</code>. As we use compiled bindings, we have to <a href="https://docs.avaloniaui.net/docs/basics/data/data-binding/compiled-bindings#type-casting">[cast the <code>DataContext</code>]</a>
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;ItemsControl ItemsSource="{Binding ToDoItems}"&gt;
    &lt;ItemsControl.ItemTemplate&gt;
        &lt;!--  The ItemTemplate defines how each item should be represented  --&gt;
        &lt;!--  Our Item will be represented by a CheckBox and a Delete-Button  --&gt;
        &lt;DataTemplate DataType="vm:ToDoItemViewModel"&gt;
            &lt;Grid ColumnDefinitions="*, Auto"&gt;
                &lt;CheckBox Content="{Binding Content}"
                          IsChecked="{Binding IsChecked}" /&gt;

                &lt;!--  Note how we use the Root-Grid to get the MainViewModel here. As we use compiled bindings we have to cast the DataContext  --&gt;
                &lt;Button Command="{Binding #Root.((vm:MainViewModel)DataContext).RemoveItemCommand}"
                        CommandParameter="{Binding .}"
                        Grid.Column="1"&gt;
                    Delete
                &lt;/Button&gt;
            &lt;/Grid&gt;
        &lt;/DataTemplate&gt;
    &lt;/ItemsControl.ItemTemplate&gt;
&lt;/ItemsControl&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>If we now run the App once again, we will see that the items are now displayed as intended. Moreover, we can now check and uncheck them as well as delete them.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/Third_Run.png" alt="Third App run">
</div>
<div class="title">Figure 5: Third App run</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_5_optional_add_some_styles_to_improve_the_ui">Step 5 (Optional): Add some Styles to improve the UI</h3>
<div class="paragraph">
<p>While our App is now fully functional and we are done with all must-haves, we can still improve the user experience. The following parts of our UI are not really user-friendly:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The Title looks exactly as any other content, this should be improved</p>
</li>
<li>
<p>The Buttons have English-only content. Having symbols would make them more understandable for all folks.</p>
</li>
<li>
<p>It would be nice, if the item that the pointer is over would be highlighted. This would help the user to click the intended item.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>For the header text we can use <a href="https://docs.avaloniaui.net/docs/reference/styles/style-selector-syntax#by-style-class">[<code>Style.Classes</code>]</a>.
In <code>App.axaml</code> add following Style:</p>
</div>
<div class="listingblock">
<div class="title">App.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Application.Styles&gt;
    &lt;!-- Do not touch this --&gt;
    &lt;FluentTheme /&gt;

    &lt;!--  Some custom Styles  --&gt;
    &lt;!--  Our header Style  --&gt;
    &lt;Style Selector="TextBlock.h1"&gt;
        &lt;Setter Property="FontWeight" Value="Bold" /&gt;
        &lt;Setter Property="FontSize" Value="15" /&gt;
        &lt;Setter Property="Margin" Value="5" /&gt;
    &lt;/Style&gt;
&lt;/Application.Styles&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Usage:</p>
</div>
<div class="listingblock">
<div class="title">Views/MainWindow.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;!--  This is our title text block. We use Style.Classes to style it accordingly  --&gt;
&lt;TextBlock Classes="h1" Text="My ToDo-List" /&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>For the <code>CheckBox</code> we want to add two different <code>Styles</code>. One that applies to each <code>CheckBox</code> and sets the <code>HorizontalAlignment</code> to fill the entire available space and another one that sets a highlight color to it&#8217;s background on pointer-over.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Avalonia has pseudo-class selectors that can be used to style a control according to its visual state. See <a href="https://docs.avaloniaui.net/docs/reference/styles/pseudo-classes">[docs]</a> for more info.
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Sometimes you need to apply a style to a visual child of the control template (see <a href="https://docs.avaloniaui.net/docs/reference/styles/pseudo-classes">[docs]</a>).
To understand which selector to use, you may use <a href="https://docs.avaloniaui.net/docs/guides/implementation-guides/developer-tools">[developer tools]</a> (use the visual tree tab).
Moreover, you can look up the original styles on <a href="https://github.com/AvaloniaUI/Avalonia/tree/master/src/Avalonia.Themes.Fluent/Controls">[GitHub in source]</a></p>
</div>
<div class="paragraph">
<p>Press <code>[F12]</code> and you can expand the visual tree to see the visual children of all controls in your view</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/DevTools.png" alt="DevTools">
</div>
<div class="title">Figure 6: DevTools in action</div>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Instead of hard-coding colors and brushes, you can use <a href="https://docs.avaloniaui.net/docs/guides/styles-and-resources/resources#using-resources">[<code>DynamicResources</code>]</a> which will make sure style follows the overall App-design. To explore the available ones from <code>FluentTheme</code>, look them up on <a href="https://github.com/AvaloniaUI/Avalonia/tree/master/src/Avalonia.Themes.Fluent/Accents">[GitHub in source]</a>
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">App.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Application.Styles&gt;
    &lt;!-- ... other styles ... --&gt;
    &lt;!--  We want our CheckBox to be stretched horizontally (the default is left-aligned)  --&gt;
    &lt;Style Selector="CheckBox"&gt;
        &lt;Setter Property="HorizontalAlignment" Value="Stretch" /&gt;
    &lt;/Style&gt;

    &lt;!--  These styles add some useful feedback for the user  --&gt;
    &lt;Style Selector="CheckBox:pointerover /template/ Grid#RootGrid"&gt;
        &lt;Setter Property="Background" Value="{DynamicResource SystemAccentColorLight3}" /&gt;
    &lt;/Style&gt;
&lt;/Application.Styles&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>To display icons we can use <code>PathIcon</code> which accepts any path-data. This data can be taken from a svg-file.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
If you use an icon from one of several online available icon galleries, make sure the license suits your needs.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">App.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Application.Resources&gt;
    &lt;!--  These are re-usable Icon data. You can get the path data from svg-files for example  --&gt;
    &lt;StreamGeometry x:Key="DeleteIconData"&gt;The path data&lt;/StreamGeometry&gt;
    &lt;StreamGeometry x:Key="AcceptIconData"&gt;The path data&lt;/StreamGeometry&gt;
&lt;/Application.Resources&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>To display the icons in our App we add them as our <code>Buttons</code> content:</p>
</div>
<div class="listingblock">
<div class="title">Views/MainWindow.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;!-- The same applies for the Delete-Button --&gt;
&lt;Button Command="{Binding AddItemCommand}"&gt;
    &lt;PathIcon Data="{DynamicResource AcceptIconData}" Foreground="Green" /&gt;
&lt;/Button&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>We want our icons to become semi-transparent if a <code>Button</code> is disabled. We can use yet another <code>Style</code> for that:</p>
</div>
<div class="listingblock">
<div class="title">App.axaml</div>
<div class="content">
<pre class="highlight"><code class="language-xml" data-lang="xml">&lt;Application.Styles&gt;
    &lt;!-- ... --&gt;

    &lt;!-- This style will make the icon semi-transparent, if a button is disabled --&gt;
    &lt;Style Selector="Button:disabled PathIcon"&gt;
        &lt;Setter Property="Opacity" Value="0.4" /&gt;
    &lt;/Style&gt;
&lt;/Application.Styles&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>And here is the final result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="_docs/Final_Result.png" alt="Final result">
</div>
<div class="title">Figure 6: The final result</div>
</div>
</div>
<div class="sect2">
<h3 id="_step_7_optional_data_persistence">Step 7 (Optional): Data Persistence</h3>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
We will be storing the data as a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.text.json?view=net-8.0">[JSON-File]</a>. You can apply the same approach to any other file-format you prefer.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Right now every time we close the App, all data is lost. For a simple demo that is all fine, but what if we wanted to start using this App for our daily to-do&#8217;s? Well, we can add basic data persistence as we already made sure our <code>Model</code> is able to handle that. For the actual file-operations we will use a helper class which is used as a service. So add a folder called <code>Services</code> and inside a new class called <code>ToDoListFileService.cs</code>.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
We can make this class <code>static</code> in our case. In a more complex App this may better be handled using <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">[Dependency Injection]</a>
</td>
</tr>
</table>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
We use a hard-coded filename here. This may not be the perfect solution, as the path may vary by user settings or platform limitations. However, this is not a part we want to cover in this sample
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This is how we can save a list of <code>Todo-Items</code>:</p>
</div>
<div class="listingblock">
<div class="title">Services/ToDoListFileService</div>
<div class="content">
<pre class="highlight"><code class="language-c#" data-lang="c#">using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using SimpleToDoList.Models;

public static class ToDoListFileService
{
    // This is a hard coded path to the file. It may not be available on every platform. In your real world App you may
    // want to make this configurable
    private static string _jsonFileName =
        Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "Avalonia.SimpleToDoList", "MyToDoList.txt");

    /// &lt;summary&gt;
    /// Stores the given items into a file on disc
    /// &lt;/summary&gt;
    /// &lt;param name="itemsToSave"&gt;The items to save&lt;/param&gt;
    public static async Task SaveToFileAsync(IEnumerable&lt;ToDoItem&gt; itemsToSave)
    {
        // Ensure all directories exists
        Directory.CreateDirectory(Path.GetDirectoryName(_jsonFileName)!);

        // We use a FileStream to write all items to disc
        using (var fs = File.Create(_jsonFileName))
        {
            await JsonSerializer.SerializeAsync(fs, itemsToSave);
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>To actually save the file, we need a way to call the <code>SaveToFileAsync</code>-method when the App is terminating. As we only target <code>Desktop</code>, we can do so in an event called <code>ShutdownRequested</code>. Moreover, we need a reference to the <code>MainViewModel</code> to be accessible form the event, and thus we store it in a private field instead of creating in inline.</p>
</div>
<div class="listingblock">
<div class="title">App.axaml.cs</div>
<div class="content">
<pre class="highlight"><code class="language-c#" data-lang="c#">// This is a reference to our MainViewModel which we use to save the list on shutdown. You can also use Dependency Injection
// in your App.
private readonly MainViewModel _mainViewModel = new MainViewModel();

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
    desktop.MainWindow = new MainWindow
    {
        DataContext = _mainViewModel // Remember to change this line to use our private reference to the MainViewModel
    };

    // Listen to the ShutdownRequested-event
    desktop.ShutdownRequested += DesktopOnShutdownRequested;
}

base.OnFrameworkInitializationCompleted();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In the event itself we cancel the event as long as <code>_canClose</code> is not set to true.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
It is highly recommended to run I/O-operations <a href="https://learn.microsoft.com/en-us/dotnet/standard/io/asynchronous-file-i-o">[<code>async</code>]</a>.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">App.axaml.cs</div>
<div class="content">
<pre class="highlight"><code class="language-c#" data-lang="c#">// We want to save our ToDoList before we actually shutdown the App. As File I/O is async, we need to wait until file is closed
// before we can actually close this window

private bool _canClose; // This flag is used to check if window is allowed to close
private async void DesktopOnShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
{
    e.Cancel = !_canClose; // cancel closing event first time

    if (!_canClose)
    {
        // To save the items, we map them to the ToDoItem-Model which is better suited for I/O operations
        var itemsToSave = _mainViewModel.ToDoItems.Select(item =&gt; item.GetToDoItem());
        await ToDoListFileService.SaveToFileAsync(itemsToSave);

        // Set _canClose to true and Close this Window again
        _canClose = true;
        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.Shutdown();
        }
    }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you run the App, add some items and close it, you should see a new file in <code>%APPDATA%\Avalonia.SimpleToDoList</code> called <code>MyToDoList.txt</code> with a content similar to this:</p>
</div>
<div class="listingblock">
<div class="title">MyToDoList.txt</div>
<div class="content">
<pre class="highlight"><code class="language-json" data-lang="json">[
    {
        "Content": "Hello Avalonia",
        "IsChecked": true
    },
    {
        "Content": "Hello World",
        "IsChecked": false
    }
]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Loading the stored file can also be done in a similar way:</p>
</div>
<div class="listingblock">
<div class="title">Services/ToDoListFileService</div>
<div class="content">
<pre class="highlight"><code class="language-c#" data-lang="c#">public static class ToDoListFileService
{
    // ...

    /// &lt;summary&gt;
    /// Loads the file from disc and returns the items stored inside
    /// &lt;/summary&gt;
    /// &lt;returns&gt;An IEnumerable of items loaded or null in case the file was not found&lt;/returns&gt;
    public static async Task&lt;IEnumerable&lt;ToDoItem&gt;?&gt; LoadFromFileAsync()
    {
        try
        {
            // We try to read the saved file and return the ToDoItemsList if successful
            using (var fs = File.OpenRead(_jsonFileName))
            {
                return await JsonSerializer.DeserializeAsync&lt;IEnumerable&lt;ToDoItem&gt;&gt;(fs);
            }
        }
        catch (Exception e) when (e is FileNotFoundException || e is DirectoryNotFoundException)
        {
            // In case the file was not found, we simply return null
            return null;
        }
    }
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">App.axaml.cs</div>
<div class="content">
<pre class="highlight"><code class="language-c#" data-lang="c#">public override async void OnFrameworkInitializationCompleted()
{
    // ...

    // Init the MainViewModel
    await InitMainViewModelAsync();
}

// Optional: Load data from disc
private async Task InitMainViewModelAsync()
{
    // get the items to load
    var itemsLoaded = await ToDoListFileService.LoadFromFileAsync();

    if (itemsLoaded is not null)
    {
        foreach (var item in itemsLoaded)
        {
            _mainViewModel.ToDoItems.Add(new ToDoItemViewModel(item));
        }
    }
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_related">Related</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you want to explore more samples, please also visit <a href="https://github.com/AvaloniaCommunity/awesome-avalonia" class="bare">https://github.com/AvaloniaCommunity/awesome-avalonia</a>.</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2025-11-07 18:43:23 UTC
</div>
</div>
</body>
</html>